#!/usr/bin/perl

{
  my $i = 1;
  sub gensym {
    "__".$i++;
  }
}

sub with_file {
  my ($file, $p) = @_;
  open IN, "< ".$file or die 'not file "'.$file.'"';
  &$p(*IN);
  close IN;
}

package Entry;

@entrys;
%entry;
{
  my $id = 0;
  sub new {
    my ($c, $name, $s) = @_;
    $s->{id} = "_".$id++;
    $s->{name} = $name;
    push @entrys, $s;
    $entry{$name} = $s;
    bless $s, $c;
  }
}

sub get {
  my $a = shift;
  $entry{$a} or die "'".$a."' symbol not found"
}

sub code {
  '&&'.shift->{id}
}

sub name {
  shift->{name}
}

sub load {
  main::with_file(shift, sub {
    my $IN = shift;
    while (<$IN>) {
      if (m#^: *([^ ]+) *(immediate)? *\((.*) *- *(.*)\)#) {
        new Entry $1, {immediate=>$2, primitive=>1};
      }
    }
  });
}

sub label_data {
  my $x;
  for my $i (@entrys) {
    $x .= 'X("'.$i->name.'", '.$i->{id}.');'."\n";
  }
  $x
}

package main;

sub convert {
  sub e {
    Entry::get shift;
  }
  my $x;
  with_file(shift, sub {
    my $IN = shift;
    while (<$IN>) {
      # ラベルの参照
      s/\$code\(([^)]+)\)/e($1)->code/eg;
      # 関数呼出し
      if (m/(\W*)\$call\(([^ ]+), (.+)?\);$/) {
        my $s = gensym;
        my $returnlabel = $s."_rl";
        my $ip = $s."_ip";
        my @a = split m/, */, $3;
        my $n = $2;
        my $indent = $1;
        my $id = e($n)->{id};

        $x .= $indent."/* call $n */\n";
        $x .= $indent."static fl_cell ".$ip."[1] = {CELL(&&".$returnlabel.")};\n";
        $x .= $indent."PUSH(RP, IP);\n";
        $x .= $indent."IP_SET(".$ip.");\n";
        my $xxx = $indent;
        $x .= $xxx."PUSH(SP, $_); " and $xxx='' for (@a);
        $x .= $indent."goto ".$id."; ".$returnlabel.":\n";
        $x .= $indent."IP_SET(POP(RP));\n";
      } else {
        if (m/^}$/) {
          $x .= "  NEXT;\n";
          $x .= "}\n";
        } else {
          $x .= $_
        }
      }
    }
  });
  $x;
}

sub wrap {
  my ($n, $c) = @_;
  "#if defined(".$n."_LOAD)\n".
  "#undef ".$n."_LOAD\n".
   $c.
  "#endif\n";
}

my $file = 'instruction.c';

Entry::load $file;

print wrap('LABEL', Entry::label_data);
print wrap('CODE', convert($file));
